如何实现单实例应用?second-instance 事件怎么用?
考察点
- 单实例锁 API 的使用
- 二次启动时的窗口聚焦与参数透传(agent 场景高频需求)
参考答案
基本实现
const { app } = require('electron');
// 请求单实例锁,必须在 ready 之前调用
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
// 没拿到锁说明已有实例在运行,直接退出
app.quit();
} else {
// 已有实例时,第二个实例启动会触发该事件
app.on('second-instance', (event, argv, workingDirectory) => {
const mainWindow = getMainWindow();
if (mainWindow) {
// 窗口最小化时先恢复
if (mainWindow.isMinimized()) mainWindow.restore();
mainWindow.show();
mainWindow.focus();
}
// 解析第二个实例携带的命令行参数(如 deeplink 唤起)
handleCommandLine(argv);
});
app.whenReady().then(createWindow);
}
Agent 场景的典型用法
1. 二次启动聚焦已有窗口 用户双击桌面图标时,agent 已在后台运行 → 聚焦主窗口而不是再开一个。
2. 解析命令行参数 / DeepLink 唤起
- Windows 上 deeplink(如
myapp://do-task?id=1)唤起已运行的实例时,URL 会作为 argv 的最后一个参数传进来:
function handleCommandLine(argv) {
// 约定:deeplink 在参数末尾
const deeplink = argv.find((arg) => arg.startsWith('myapp://'));
if (deeplink) {
handleDeeplink(deeplink); // 分发业务路由
}
}
- macOS 不走 argv,而是
app.on('open-url')事件(需先用app.setAsDefaultProtocolClient注册协议)。
注意点
requestSingleInstanceLock必须在ready之前调用,否则时机太晚。second-instance的回调里窗口可能已销毁,要先判空。- Windows 安装包(Squirrel 时代)的更新快捷方式可能带特殊参数(
--squirrel-firstrun等),注意过滤。 - 打包后的 argv 与开发环境不同,取参数时跳过前几项(开发模式前两项是 electron 和入口路径)。
常见追问
- 想做多实例怎么办? 不请求锁即可,或者用
process.env传 profile 实现多开隔离(多开时 userData 目录要分开,否则会报 profile 锁错误)。 - 锁的粒度? 默认按 userData 目录(即同一应用不同用户数据目录不算同一实例)。